package com.github.live.manager;

import android.util.ArrayMap;

import com.github.live.util.store.HawkConfig;
import com.github.live.util.store.HawkStorage;
import com.github.livebase.storage.LocalStorage;
import com.github.livebase.bean.Channel;
import com.github.livebase.bean.ChannelGroup;
import com.github.livebase.bean.EpgInformation;
import com.github.livebase.event.Event;
import com.github.livebase.event.EventBus;
import com.github.livebase.event.EventListener;
import com.github.livebase.event.RegistrableListener;
import com.github.livebase.util.base.CollectionsUtil;
import com.github.livebase.util.base.ObjectsUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @ClassName ChannelsManager
 * @description: 频道管理器
 * @author: biao532
 * @create: 2023-12-17 14:10
 * @Version 1.0
 **/
public class ChannelsManager extends RegistrableListener implements EventListener {
    @Override
    public void onEvent(Event event) {
        // 每分钟更新配置信息
        if (!Event.isEvent(event, EventBus.EVENT_CHANGE_TIME) || this.sourceIndexMap == null) {
            return;
        }
        HawkStorage.getStore().put(HawkConfig.LIVE_SOURCE_MAP, this.sourceIndexMap);
    }

    public interface ChangeEvent {
        interface DefaultChangeEvent extends ChangeEvent {
            @Override
            default void onChannelChange(Channel oldChannel, Channel newChannel){};

            @Override
            default void onSourceChange(Channel.Source oldSource, Channel.Source newSource){};

            @Override
            default void onLiveChange(Channel.Source source, EpgInformation information, boolean isLive){};

            @Override
            default void onNoChange(){};
        }
        /**
         * 频道改变事件
         * @param oldChannel
         * @param newChannel
         */
        void onChannelChange(Channel oldChannel, Channel newChannel);

        /**
         * 源改变事件
         * @param oldSource
         * @param newSource
         */
        void onSourceChange(Channel.Source oldSource, Channel.Source newSource);

        /**
         * 直播状态改变事件
         * @param source
         * @param information
         * @param isLive
         */
        void onLiveChange(Channel.Source source, EpgInformation information, boolean isLive);

        /**
         * 有操作，但是无改变
         */
        void onNoChange();
    }
    // 直播 / 回看
    private boolean isLive = true;
    private List<ChannelGroup> groups;
    // 当前组索引
    private int groupIndex = 0;
    // 当前频道在组中的索引
    private int channelIndex = 0;
    // 用于保存上一个频道
    private int preUserChannel;
    // 频道正在使用的源的配置
    private Map<String, Integer> sourceIndexMap;
    // 注册的事件
    private List<ChangeEvent> changeEvents;
    public static final int DIRECTION_DOWN = 0;
    public static final int DIRECTION_UP = 1;
    private int direction;
    protected ChannelsManager(List<ChannelGroup> groups) {
        super(EventBus.EVENT_CHANGE_TIME, true);
        LocalStorage store = HawkStorage.getStore();
        this.sourceIndexMap = store.get(HawkConfig.LIVE_SOURCE_MAP, new ArrayMap<>());
        int s = store.get(HawkConfig.LIVE_CHANNEL, 0);
        this.preUserChannel = s;
        // 前12bit为组索引
        this.groupIndex = (s >> 20) & 0xfff;
        // 后20bit为组内索引
        this.channelIndex = s & 0xfffff;
        this.groups = groups;
    }

    /**
     * 是否无任何频道
     * @return
     */
    public boolean isEmptyChannels() {
        return CollectionsUtil.isEmpty(groups);
    }

    /**
     * 注册事件监听
     * @param changeEvent
     */
    public void addChangeEvent(ChangeEvent changeEvent) {
        if (CollectionsUtil.isEmpty(this.changeEvents)) {
            this.changeEvents = new ArrayList<>(1);
        }
        if (this.changeEvents.contains(changeEvent)) {
            return;
        }
        this.changeEvents.add(changeEvent);
    }

    /**
     * 移除事件监听
     * @param changeEvent
     */
    public void removeChangeEvent(ChangeEvent changeEvent) {
        if (CollectionsUtil.isEmpty(this.changeEvents)) {
            return;
        }
        this.changeEvents.remove(changeEvent);
    }

    /**
     * 是否为直播
     * @return
     */
    public boolean isLive() {
        return isLive;
    }

    /**
     * 获取所有组
     * @return
     */
    public List<ChannelGroup> getLiveChannelGroups() {
        return this.groups;
    }

    /**
     * 获取当前组索引
     * @return
     */
    public int getCurrentGroupIndex() {
        return this.groupIndex;
    }

    /**
     * 获取当前组
     * @return
     */
    public ChannelGroup getCurrentGroup() {
        return CollectionsUtil.getOrDefault(groups, this.groupIndex, null);
    }

    /**
     * 获取当前组内索引
     * @return
     */
    public int getCurrentChannelIndex() {
        return this.channelIndex;
    }

    /**
     * 获取当前频道
     * @return
     */
    public Channel getCurrentChannel() {
        return getChannelByGroupIndexAndChannelIndex(this.groupIndex, this.channelIndex);
    }

    /**
     * 获取当前组的所有频道
     * @return
     */
    public List<Channel> getCurrentChannels() {
        return getChannelsByGroupIndex(this.groupIndex);
    }

    /**
     * 根据组索引和组内索引进行跳转
     * @param groupIndex
     * @param channelIndex
     */
    public void setActiveByGroupIndexAndChannelIndex(int groupIndex, int channelIndex) {
        if (!isLive) {
            replayOver();
        }
        if (this.groupIndex == groupIndex && this.channelIndex == channelIndex) {
            CollectionsUtil.foreach(changeEvents, (e)-> e.onNoChange());
            return;
        }
        Channel old = getChannelByGroupIndexAndChannelIndex(this.groupIndex, this.channelIndex);
        Channel.Source oldSource = getCurrentSource();
        Channel nChannel = getChannelByGroupIndexAndChannelIndex(groupIndex, channelIndex);
        if (nChannel == null) {
            CollectionsUtil.foreach(changeEvents, (e)-> e.onNoChange());
            return;
        }

        this.preUserChannel = (this.groupIndex << 20) + (this.channelIndex & 0xfffff);

        this.groupIndex = groupIndex;
        this.channelIndex = channelIndex;
        HawkStorage.getStore().put(HawkConfig.LIVE_CHANNEL, (this.groupIndex << 20) + (this.channelIndex & 0xfffff));
        Channel.Source currentSource = getCurrentSource();
        CollectionsUtil.foreach(changeEvents, (e)-> {
            e.onChannelChange(old, nChannel);
            e.onSourceChange(oldSource, currentSource);
        });
    }

    /**
     * 根据频道号设置进行跳转
     * @param number
     */
    public void setActiveByNumber(int number) {
        for (int i = 0, l = groups.size(); i < l; i++) {
            List<Channel> channels = getChannelsByGroupIndex(i);
            Channel first = channels.get(0);
            if (first.getNumber() <= number && channels.get(channels.size()-1).getNumber() >= number) {
                int channelIndex = number - first.getNumber();
                setActiveByGroupIndexAndChannelIndex(i, channelIndex);
                return;
            }
        }
        setActiveByGroupIndexAndChannelIndex(this.groupIndex, this.channelIndex);
    }

    /**
     * 前一个观看的频道
     */
    public void backChannel() {
        setActiveByGroupIndexAndChannelIndex((preUserChannel >> 20) & 0xfff, preUserChannel & 0xfffff);
    }

    /**
     * 获取当前频道的当前源索引
     * @return
     */
    public int getCurrentSourceIndex() {
        Channel c = getCurrentChannel();
        if (c == null) return 0;
        String key = c.getId();
        int index = ObjectsUtil.getOrDefault(sourceIndexMap.get(key), 0);
        if (index < 0 || index >= getCurrentChannel().getSources().size()) {
            index = 0;
            sourceIndexMap.put(key, 0);
        }
        return index;
    }

    /**
     * 获取当前频道的当前源
     * @return
     */
    public Channel.Source getCurrentSource() {
        Channel channel = getCurrentChannel();
        if (channel == null) {
            return null;
        }
        return channel.getSources().get(getCurrentSourceIndex());
    }

    /**
     * 根据索引设置当前频道的当前源
     * @param sourceIndex
     */
    public void setCurrentChannelSource(int sourceIndex) {
        String key = getCurrentChannel().getId();
        int index = ObjectsUtil.getOrDefault(sourceIndexMap.get(key), 0);
        Channel channel = getCurrentChannel();
        if (sourceIndex < 0) {
            sourceIndex = channel.getSources().size()-1;
        }
        if (sourceIndex >= channel.getSources().size()) {
            sourceIndex = 0;
        }
        if (sourceIndex == index) {
            CollectionsUtil.foreach(changeEvents, e->e.onNoChange());
            return;
        }
        if (!isLive) {
            replayOver();
        }
        sourceIndexMap.put(key, sourceIndex);

        Channel.Source old = channel.getSources().get(index);
        Channel.Source newSource = channel.getSources().get(sourceIndex);
        CollectionsUtil.foreach(changeEvents, e-> {
            e.onSourceChange(old, newSource);
        });

    }

    /**
     * 根据 组索引 和 组内索引获取频道信息
     * @param groupIndex
     * @param channelIndex
     * @return
     */
    public Channel getChannelByGroupIndexAndChannelIndex(int groupIndex, int channelIndex) {
        return CollectionsUtil.getOrDefault(getChannelsByGroupIndex(groupIndex), channelIndex, null);
    }

    /**
     * 切换至下一个频道
     */
    public void nextChannel() {
        direction = DIRECTION_DOWN;
        int channelIndex = this.channelIndex + 1;
        int groupIndex = this.groupIndex;
        if (channelIndex >= getCurrentChannels().size()) {
            channelIndex = 0;
            groupIndex ++;
            if (groupIndex >= groups.size()) {
                groupIndex = 0;
            }
        }
        setActiveByGroupIndexAndChannelIndex(groupIndex, channelIndex);
    }

    public int getDirection() {
        return direction;
    }

    /**
     * 切换至上一个频道
     */
    public void preChannel() {
        direction = DIRECTION_UP;
        int channelIndex = this.channelIndex - 1;
        int groupIndex = this.groupIndex;
        if (channelIndex < 0) {
            groupIndex --;
            if (groupIndex < 0) {
                groupIndex = this.groups.size() - 1;
            }
            channelIndex = getChannelsByGroupIndex(groupIndex).size() - 1;
        };
        setActiveByGroupIndexAndChannelIndex(groupIndex, channelIndex);
    }

    /**
     * 切换至下一个源
     */
    public void nextSource() {
        setCurrentChannelSource(getCurrentSourceIndex()+1);
    }

    /**
     * 切换至上一个源
     */
    public void preSource() {
        setCurrentChannelSource(getCurrentSourceIndex()-1);
    }

    /**
     * 根据 组索引 获取组内所有频道列表
     * @param groupIndex
     * @return
     */
    public List<Channel> getChannelsByGroupIndex(int groupIndex) {
        ChannelGroup group = CollectionsUtil.getOrDefault(groups, groupIndex, null);
        if (group == null || group.getChannels() == null) {
            return ObjectsUtil.Empty.LIST;
        }
        return group.getChannels();
    }

    /**
     * 结束回看
     */
    public void replayOver() {
        if (this.isLive) {
            return;
        }
        Channel.Source s = getCurrentSource();
        this.isLive = true;
        CollectionsUtil.foreach(changeEvents, (e)-> {
            e.onLiveChange(s, null,  this.isLive);
        });
    }

    /**
     * 根据information信息设置为回看
     * @param information
     */
    public void replay(EpgInformation information) {
        Channel.Source s = getCurrentSource();
        if (!s.supportedReplay()) {
            return;
        }
        this.isLive = false;
        CollectionsUtil.foreach(changeEvents, (e)-> {
            e.onLiveChange(s, information,  this.isLive);
        });
    }

    /**
     * 根据filter获取首个指定的频道
     * @param filter
     * @return
     */
    public static Channel getChannelByFilter(CollectionsUtil.Filter<Channel> filter) {
        ChannelsManager cm = LiveInitializer.getInitializer().getChannelsManager();
        List<ChannelGroup> groups = cm.getLiveChannelGroups();
        for (int i = 0, l = groups.size(); i < l; i++) {
            ChannelGroup group = groups.get(i);
            int channelIndex = CollectionsUtil.firstIndex(group.getChannels(), filter);
            if (channelIndex == -1) {
                continue;
            }
            return cm.getChannelByGroupIndexAndChannelIndex(i, channelIndex);
        }
        return null;
    }
}
